import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 连接池工具类,使用单例模式，确保只有一个数据库连接池
 *
 * @author HHLJ
 * @date 2022/03/16
 */
public class ConnectionPoolUtil extends AbstractConnectionPool {
    private static volatile ConnectionPoolUtil connectionPool;
    private List<Connection> idleConnections;
    private List<Connection> activeConnections;
    private static final ReadWriteLock READ_WRITE_LOCK = new ReentrantReadWriteLock();

    private ConnectionPoolUtil() {
        try {
            createConnectionPool();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建连接池，返回一个工具类对象
     *
     * @return {@link ConnectionPoolUtil}
     */
    public static ConnectionPoolUtil getInstance(){

        try {
            READ_WRITE_LOCK.writeLock().lock();
            if (connectionPool == null) {
                connectionPool = new ConnectionPoolUtil();
                System.out.println();
            }
        } finally {
            READ_WRITE_LOCK.writeLock().unlock();
        }
        return connectionPool;
    }

    /**
     * 得到连接
     *
     * @return {@link Connection}
     */
    @Override
    public Connection getConnection(){
        //当未创建数据库连接池时返回null
        boolean flag = idleConnections == null && activeConnections == null;
        if (flag) {
            return null;
        }
        //获取可用的空闲连接
        Connection connection = getAvailableConnection();
        int num = 0;
        while (connection == null) {
            num += 1;
            waitForConnection(timeToWait);
            connection = getAvailableConnection();
            //当超时的时候返回空
            if(num > 100) {
               throw new RuntimeException("没有了");
            }
        }
        activeConnections.add(connection);
        return connection;
    }

    /**
     * 释放
     *
     * @param connection        连接
     * @param preparedStatement 事先准备好的声明中
     * @param resultSet         结果集
     */
    public void release(Connection connection, PreparedStatement preparedStatement, ResultSet resultSet){
        returnConnection(connection);
        if(preparedStatement != null) {
            try {
                preparedStatement.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 返回连接
     *
     * @param connection 连接
     */
    private void returnConnection(Connection connection){
        try {
            READ_WRITE_LOCK.writeLock().lock();
            activeConnections.remove(connection);
            if (idleConnections.size() < maxIdleConnections){
                idleConnections.add(connection);
            } else {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            READ_WRITE_LOCK.writeLock().unlock();
        }
    }

    /**
     * 得到可用的连接
     *
     * @return {@link Connection}
     */
    private Connection getAvailableConnection() {

        Connection connection = null;
        //当激活连接池中连接数大于最大连接数时返回空
        if (activeConnections.size() >= maxActiveConnections) {
            return null;
        }
        //如果空闲连接池不为空，直接获取连接
        try {
            READ_WRITE_LOCK.writeLock().lock();
            if (isNotEmpty(idleConnections)) {
                connection = idleConnections.remove(0);
            } else {
                try {
                    //如果为空自动创建相应数量的数据库连接
                    createConnections(incrementConnections);
                    if(isNotEmpty(idleConnections)) {
                        connection = idleConnections.remove(0);
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        } finally {
            READ_WRITE_LOCK.writeLock().unlock();
        }

        return connection;
    }

    /**
     * 创建连接池，为idleConnections和activeConnections创建CopyOnWriteArrayList对象
     *
     * @throws SQLException sqlexception异常
     */
    private void createConnectionPool() throws SQLException {
        idleConnections = new CopyOnWriteArrayList<>();
        activeConnections = new CopyOnWriteArrayList<>();
        createConnections(maxIdleConnections);
    }

    /**
     * 创建新连接
     *
     * @return {@link Connection}
     * @throws SQLException sqlexception异常
     */
    private Connection newConnection() throws SQLException {
        try {
            READ_WRITE_LOCK.writeLock().lock();
            return super.getConnection();
        } finally {
            READ_WRITE_LOCK.writeLock().unlock();
        }
    }

    /**
     * 按数量创建连接
     *
     * @param numConnections num连接
     * @throws SQLException sqlexception异常
     */
    private void createConnections(int numConnections) throws SQLException {
        for (int i = 0; i < numConnections; i++){
            boolean flag = (size(idleConnections) + size(activeConnections)) <= maxActiveConnections;
            try {
                READ_WRITE_LOCK.writeLock().lock();
                if (flag) {
                    idleConnections.add(newConnection());
                }
            } finally {
                READ_WRITE_LOCK.writeLock().unlock();
            }
        }
    }

    /**
     * 等待连接
     *
     * @param millis 米尔斯
     */
    private void waitForConnection(int millis){
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 大小
     *
     * @param list 列表
     * @return int
     */
    private int size(List<Connection> list) {
        READ_WRITE_LOCK.readLock().lock();
        try {
            return list.size();
        } finally {
            READ_WRITE_LOCK.readLock().unlock();
        }
    }

    /**
     * 不是空的
     *
     * @param list 列表
     * @return boolean
     */
    private boolean isNotEmpty(List<Connection> list){
        READ_WRITE_LOCK.readLock().lock();
        try {
            return !list.isEmpty();
        } finally {
            READ_WRITE_LOCK.readLock().unlock();
        }
    }
}
